import ConvexGeometry from "./convex-geometry";
import CONSTANT from '../constant';

export default class CylinderGeometry extends ConvexGeometry{
    constructor(radius,halfHeight) {
        super(CONSTANT.GEOMETRY_TYPE_CYLINDER);
        this._radius = radius;
        this._halfHeight = halfHeight;
        this._updateMass();
    }

    //......................................public.........................................

    getRadius() {
        return this._radius;
    }
    getHalfHeight() {
        return this._halfHeight;
    }

    //......................................private.........................................

    _updateMass() {
        let r=this._radius,h=this._halfHeight;
        let r2 = r * r;
        let h2 = h * h * 4;
        this._volume = 3.14159265358979 * r2 * h * 2;
        let icf=this.inertiaCoeff;
        icf[0] = 0.083333333333333329 * (3 * r2 + h2);
        icf[1] = 0;
        icf[2] = 0;
        icf[3] = 0;
        icf[4] = 0.5 * r2;
        icf[5] = 0;
        icf[6] = 0;
        icf[7] = 0;
        icf[8] = 0.083333333333333329 * (3 * r2 + h2);
    }
    _computeAabb(_aabb,_tf) {
        let r=this._radius,h=this._halfHeight;
        let aabb=_aabb.elements,tf=_tf.elements;
        let axisX = tf[4],axisY = tf[7],axisZ = tf[10];
        if(axisX < 0) axisX = -axisX;
        if(axisY < 0) axisY = -axisY;
        if(axisZ < 0) axisZ = -axisZ;
        let axis2X = axisX * axisX,axis2Y = axisY * axisY,axis2Z = axisZ * axisZ;
        let erX = Math.sqrt(1 - axis2X),erY = Math.sqrt(1 - axis2Y),erZ = Math.sqrt(1 - axis2Z);
        erX *= r;erY *= r;erZ *= r;
        let ehX = axisX * h,ehY = axisY * h,ehZ = axisZ * h;
        let maxX = erX + ehX,maxY = erY + ehY,maxZ = erZ + ehZ;
        aabb[0] = tf[0] - maxX;aabb[1] = tf[1] - maxY;aabb[2] = tf[2] - maxZ;
        aabb[3] = tf[0] + maxX;aabb[4] = tf[1] + maxY;aabb[5] = tf[2] + maxZ;
    }
    computeLocalSupportingVertex(_dir,_out) {
        let dir=_dir.elements,out=_out.elements;
        let rx = dir[0];
        let rz = dir[2];
        let len = rx * rx + rz * rz;
        let coreRadius = this._radius - this._gjkMargin;
        if(coreRadius < 0) coreRadius = 0;
        let invLen = len > 0 ? coreRadius / Math.sqrt(len) : 0;
        let coreHeight = this._halfHeight - this._gjkMargin;
        if(coreHeight < 0) coreHeight = 0;
        out[0] = rx * invLen;
        out[1] = dir[1] > 0 ? coreHeight : -coreHeight;
        out[2] = rz * invLen;
    }
    _rayCastLocal(beginX,beginY,beginZ,endX,endY,endZ,hit) {
        let halfH = this._halfHeight;
        let dx = endX - beginX, dy = endY - beginY, dz = endZ - beginZ;
        let tminy = 0, tmaxy = 1;
        if(dy > -1e-6 && dy < 1e-6) {
            if(beginY <= -halfH || beginY >= halfH) return false;
        } else {
            let invDy = 1 / dy;
            let t1 = (-halfH - beginY) * invDy;
            let t2 = (halfH - beginY) * invDy;
            if(t1 > t2) {
                let tmp = t1;
                t1 = t2;
                t2 = tmp;
            }
            if(t1 > 0) tminy = t1;
            if(t2 < 1) tmaxy = t2;
        }
        if(tminy >= 1 || tmaxy <= 0) return false;
        let tminxz = 0;
        let tmaxxz;
        let a = dx * dx + dz * dz;
        let b = beginX * dx + beginZ * dz;
        let c = beginX * beginX + beginZ * beginZ - this._radius * this._radius;
        let D = b * b - a * c;
        if(D < 0) return false;
        if(a > 0) {
            let sqrtD = Math.sqrt(D);
            tminxz = (-b - sqrtD) / a;
            tmaxxz = (-b + sqrtD) / a;
            if(tminxz >= 1 || tmaxxz <= 0) return false;
        } else {
            if(c >= 0) return false;
            tminxz = 0;
            tmaxxz = 1;
        }
        let min;
        if(tmaxxz <= tminy || tmaxy <= tminxz) return false;
        let normal=hit.normal.elements;
        let position=hit.position.elements;
        if(tminxz < tminy) {
            min = tminy;
            if(min === 0) return false;
            normal[0]=0;normal[1]=dy > 0 ? -1 : 1;normal[2]=0;
        } else {
            min = tminxz;
            if(min === 0) return false;
            normal[0]=beginX + dx * min;normal[1]=0;normal[2]=beginZ + dz * min;
            let invLen = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
            if(invLen > 0) {
                invLen = 1 / invLen;
            }
            normal[0] *= invLen;normal[1] *= invLen;normal[2] *= invLen;
        }
        position[0]=beginX + min * dx;position[1]=beginY + min * dy;position[2]=beginZ + min * dz;
        hit.fraction = min;
        return true;
    }
}